﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using TFSRequestManager.TfsAdministration;
using System.Collections.Specialized;
using System.IO;
using System.Diagnostics;

namespace TFSRequestManager
{
    public partial class MainForm : Form
    {
        #region Constants

        private const int MINIMUM_REFRESH_INTERVAL_IN_SECONDS = 5;
        private const int MAXIMUM_REFRESH_INTERVAL_IN_SECONDS = Int32.MaxValue;
        private const int MINIMUM_EXECUTION_THRESHOLD_IN_SECONDS = 1;
        private const int MAXIMUM_EXECUTION_THRESHOLD_IN_SECONDS = Int32.MaxValue;
        private const int DEFAULT_MAXIMUM_NOTIFICATIONS_LIST_SIZE = 1000;
        private const string AUTOREFRESH_TEMPLATE = "Auto-refresh every {0} seconds";
        private const string NOTIFY_THRESHOLD_TEMPLATE = "Notify when execution exceeds {0} seconds";
        private const string LAST_UPDATED_TEMPLATE = "Last updated: {0} {1}";
        private const string THRESHOLD_EXCEEDED_TEMPLATE = "Threshold exceeded [{0}]: {1}";
        private const string WEB_SERVICE_ERROR_TEMPLATE = "Error Occurred: Message: {0} - Stack Trace: {1}";
        private const string TITLE_TEMPLATE = "TFS Request Manager [{0}]";
        private const int MAXIMUM_SAMPLES_DISPLAYED = 300;
        private const string PLOT_MODE_FORMAT = "Plot Mode: {0}";

        #endregion

        #region Private Fields

        private readonly object _notificationsSyncLock = new object();
        private Timer _pollingTimer;
        private int _refreshIntervalInSeconds = 0;
        private int _executionThresholdInSeconds = 0;
        private int _maximumNotificationsListSize = DEFAULT_MAXIMUM_NOTIFICATIONS_LIST_SIZE;
        private NameValueCollection _notifications = new NameValueCollection();
        private List<PerfSample> _perfSamples = new List<PerfSample>();
        private DateTime _lastErrorAddedDateTime = DateTime.MinValue;
        private readonly object _lastErrorAddedSyncLock = new object();
        private volatile int _currentIntervalValue = 0;

        #endregion

        #region Constructor

        public MainForm()
        {
            InitializeComponent();
        }

        #endregion

        #region Event Handlers

        private void MainForm_Load(object sender, EventArgs e)
        {
            InitializeForm();
            if (AutoRefreshCheckBox.Checked)
            {
                PopulateActiveRequests(isManualRefresh: true);
            }
        }

        private void RefreshButton_Click(object sender, EventArgs e)
        {
            PopulateActiveRequests(isManualRefresh: true);
        }

        private void MainForm_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
            {
                PopulateActiveRequests(isManualRefresh: true);
                e.Handled = true;
            }
        }

        private void AutoRefreshCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (AutoRefreshCheckBox.Checked)
            {
                if (_refreshIntervalInSeconds > 0)
                {
                    InitializeTimer();
                    InitializeChart();
                    GraphInstructionsLabel.Visible = false;
                    PerfChart.Visible = true;
                    SetStatTypeLabel();
                    PlotModeToolStripStatusLabel.Visible = true;
                }
                else
                {
                    AutoRefreshCheckBox.Checked = false;
                }
            }
            else
            {
                StopTimer();
                PerfChart.Visible = false;
                GraphInstructionsLabel.Visible = true;
                PlotModeToolStripStatusLabel.Visible = false;
                _perfSamples.Clear();
            }
        }

        private void ShowNotificationsCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (ShowNotificationsCheckBox.Checked)
            {
                if (_executionThresholdInSeconds <= 0)
                {
                    ShowNotificationsCheckBox.Checked = false;
                    notificationsToolStripSplitButton.Visible = false;
                }
            }
            else
            {
                TrayNotifyIcon.Visible = false;
                notificationsToolStripSplitButton.Visible = false;
            }
        }

        private void TfsActiveRequestsDataGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex >= 0)
            {
                //long requestId = (long)TfsActiveRequestsDataGridView.Rows[e.RowIndex].Cells["RequestId"].Value;
                Guid hostId = (Guid)TfsActiveRequestsDataGridView.Rows[e.RowIndex].Cells["HostId"].Value;

                using (DetailsForm detailsForm = new DetailsForm())
                {
                    detailsForm.HostId = hostId;
                    detailsForm.ServerActivityName = (string)TfsActiveRequestsDataGridView.Rows[e.RowIndex].Cells["ServerActivityName"].Value;
                    detailsForm.ServerActivityStartTime = (DateTime)TfsActiveRequestsDataGridView.Rows[e.RowIndex].Cells["ServerActivityStartTime"].Value;
                    detailsForm.ServerActivityStatus = (int)TfsActiveRequestsDataGridView.Rows[e.RowIndex].Cells["ServerActivityStatus"].Value;
                    detailsForm.ServerActivityStatusReason = (string)TfsActiveRequestsDataGridView.Rows[e.RowIndex].Cells["ServerActivityStatusReason"].Value;
                    detailsForm.RemoteAddress = (string)TfsActiveRequestsDataGridView.Rows[e.RowIndex].Cells["RemoteAddress"].Value;
                    detailsForm.ActiveRequest = ((ActiveRequest)TfsActiveRequestsDataGridView.Rows[e.RowIndex].DataBoundItem).RequestInformation;
                    detailsForm.ShowDialog();
                }
            }
        }

        private void TrayNotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ShowNotifications();
        }

        private void showNotificationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowNotifications();
        }

        private void hideIconToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TrayNotifyIcon.Visible = false;
        }

        private void NotifyIconContextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            showNotificationsToolStripMenuItem.Enabled = (_notifications != null && _notifications.Count > 0);
            clearNotificationsToolStripMenuItem.Enabled = (_notifications != null && _notifications.Count > 0);
        }

        private void clearNotificationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            lock (_notificationsSyncLock)
            {
                _notifications.Clear();
            }
            notificationsToolStripSplitButton.Visible = false;
            TrayNotifyIcon.Visible = false;
        }

        private void copyToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Utility.CopyActiveRequestsToClipboard((IEnumerable<ActiveRequest>)TfsAdministrationBindingSource.DataSource);
        }

        private void showNotificationsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            ShowNotifications();
        }

        private void notificationsToolStripSplitButton_ButtonClick(object sender, EventArgs e)
        {
            ShowNotifications();
        }

        void MainForm_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            if (MessageBox.Show("Are you sure you want to exit?", "Exit Program", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.No)
            {
                e.Cancel = true;
            }
        }

        private void OnTimedEvent(object sender, EventArgs e)
        {
            try
            {
                _pollingTimer.Stop();
                if (_currentIntervalValue >= _refreshIntervalInSeconds)
                {
                    PopulateActiveRequests(isManualRefresh: false);
                    _currentIntervalValue = 0;
                }
                else
                {
                    StretchLastSample();
                    _currentIntervalValue++;
                }
            }
            finally
            {
                if (_pollingTimer != null)
                    _pollingTimer.Enabled = true;
            }
        }

        private void SettingsButton_Click(object sender, EventArgs e)
        {
            string configFilePath = string.Format("{0}.config", Application.ExecutablePath);

            using (Process process = new Process())
            {
                ProcessStartInfo processInfo = new ProcessStartInfo();
                processInfo.CreateNoWindow = true;
                processInfo.UseShellExecute = true;
                processInfo.WindowStyle = ProcessWindowStyle.Maximized;
                processInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "notepad.exe");
                processInfo.Arguments = configFilePath;
                processInfo.Verb = "runas"; // start the process elevated.

                process.StartInfo = processInfo;
                bool result = process.Start();
                if (result)
                {
                    process.WaitForExit();

                    MessageBox.Show("You must exit the application and launch it again for some modifications to the settings to take effect.", "Edit Settings", MessageBoxButtons.OK, MessageBoxIcon.Information);

                    Properties.Settings.Default.Reload();
                }
            }
        }

        #endregion

        #region Private Methods

        private void InitializeForm()
        {
            VersionToolStripStatusLabel.Text = string.Format("  {0}", Application.ProductVersion);

            // If the values are not within the boundary values, reset to zero. Zero is a valid value that indicates no threshold is used.
            if (Properties.Settings.Default.RefreshIntervalInSeconds < MINIMUM_REFRESH_INTERVAL_IN_SECONDS && Properties.Settings.Default.RefreshIntervalInSeconds != 0)
            {
                MessageBox.Show(string.Format("RefreshIntervalInSeconds setting ({0}) is less than the minimum allowed: {1}. Resetting to 0.", Properties.Settings.Default.RefreshIntervalInSeconds, MINIMUM_REFRESH_INTERVAL_IN_SECONDS),
                    "Invalid value for RefreshIntervalInSeconds", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                _refreshIntervalInSeconds = 0;
            }
            else if (Properties.Settings.Default.RefreshIntervalInSeconds > MAXIMUM_REFRESH_INTERVAL_IN_SECONDS)
            {
                MessageBox.Show(string.Format("RefreshIntervalInSeconds setting ({0}) is less than the minimum allowed: {1}. Resetting to 0.", Properties.Settings.Default.RefreshIntervalInSeconds, MINIMUM_REFRESH_INTERVAL_IN_SECONDS),
                    "Invalid value for RefreshIntervalInSeconds", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                _refreshIntervalInSeconds = 0;
            }
            else
            {
                _refreshIntervalInSeconds = Properties.Settings.Default.RefreshIntervalInSeconds;
            }
            AutoRefreshCheckBox.Text = string.Format(AUTOREFRESH_TEMPLATE, _refreshIntervalInSeconds);
            AutoRefreshCheckBox.Checked = Properties.Settings.Default.DefaultAutoRefreshOn;

            if (Properties.Settings.Default.ExecutionThresholdInSeconds < MINIMUM_EXECUTION_THRESHOLD_IN_SECONDS && Properties.Settings.Default.ExecutionThresholdInSeconds != 0)
            {
                MessageBox.Show(string.Format("RefreshIntervalInSeconds setting ({0}) is less than the minimum allowed: {1}. Resetting to 0.", Properties.Settings.Default.RefreshIntervalInSeconds, MINIMUM_REFRESH_INTERVAL_IN_SECONDS),
                    "Invalid value for RefreshIntervalInSeconds", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                _executionThresholdInSeconds = 0;
            }
            else if (Properties.Settings.Default.ExecutionThresholdInSeconds > MAXIMUM_EXECUTION_THRESHOLD_IN_SECONDS)
            {
                MessageBox.Show(string.Format("RefreshIntervalInSeconds setting ({0}) is less than the minimum allowed: {1}. Resetting to 0.", Properties.Settings.Default.RefreshIntervalInSeconds, MINIMUM_REFRESH_INTERVAL_IN_SECONDS),
                    "Invalid value for RefreshIntervalInSeconds", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                _executionThresholdInSeconds = 0;
            }
            else
            {
                _executionThresholdInSeconds = Properties.Settings.Default.ExecutionThresholdInSeconds;
            }
            ShowNotificationsCheckBox.Text = string.Format(NOTIFY_THRESHOLD_TEMPLATE, _executionThresholdInSeconds);
            ShowNotificationsCheckBox.Checked = Properties.Settings.Default.DefaultNotifyOnExecutionThresholdExceededOn;

            _maximumNotificationsListSize = Properties.Settings.Default.MaximumNotificationsListSize;
        }

        private void InitializeTimer()
        {
            if (_refreshIntervalInSeconds > 0)
            {
                _currentIntervalValue = 0;
                // Seed the timer with the polling interval specified in config.
                if (_pollingTimer != null)
                {
                    StopTimer();
                }
                _pollingTimer = new Timer();
                _pollingTimer.Tick += new EventHandler(OnTimedEvent);
                _pollingTimer.Interval = 1000; // 1-second interval
                _pollingTimer.Enabled = true;
            }
            else
            {
                StopTimer();
            }
        }

        private void InitializeChart()
        {
            _perfSamples = new List<PerfSample>();

            // Initialize the chart with empty values.
            for (int i = 0; i <= MAXIMUM_SAMPLES_DISPLAYED; i++)
            {
                _perfSamples.Add(new PerfSample()
                {
                    SampleNumber = (long)i,
                    AvgExecutionTime = 0
                });
            }

            RepaintChart();
        }

        private void StretchLastSample()
        {
            if (AutoRefreshCheckBox.Checked && _refreshIntervalInSeconds > 0)
            {
                long sampleNumber = _perfSamples.Count == 0 ? 0 : (_perfSamples[_perfSamples.Count - 1].SampleNumber + 1);
                double normalizedRequestExecutionTime = _perfSamples.Count == 0 ? 0 : (_perfSamples[_perfSamples.Count - 1].AvgExecutionTime);

                if (_perfSamples.Count > MAXIMUM_SAMPLES_DISPLAYED)
                {
                    _perfSamples.RemoveAt(0);
                }

                _perfSamples.Add(new PerfSample()
                {
                    SampleNumber = sampleNumber,
                    AvgExecutionTime = normalizedRequestExecutionTime
                });

                RepaintChart();
            }
        }

        private void AddPerfSample(ActiveRequest[] activeRequests)
        {
            if (AutoRefreshCheckBox.Checked && _refreshIntervalInSeconds > 0)
            {

                long sampleNumber = _perfSamples.Count == 0 ? 0 : (_perfSamples[_perfSamples.Count - 1].SampleNumber + 1);
                double normalizedRequestExecutionTime = 0;

                if (_perfSamples.Count > MAXIMUM_SAMPLES_DISPLAYED)
                {
                    _perfSamples.RemoveAt(0);
                }

                if (activeRequests != null)
                {
                    ExecutionThresholdType executionThresholdType = GetExecutionThresholdType();

                    double sampleValue = 0;
                    switch (executionThresholdType)
                    {
                        case ExecutionThresholdType.Avg:
                            sampleValue = activeRequests.Average(sample => (sample.ExecutionTime / 1000));
                            break;
                        case ExecutionThresholdType.Median:
                            sampleValue = activeRequests.Select<ActiveRequest, double>(sample => (sample.ExecutionTime / 1000)).Median();
                            break;
                        case ExecutionThresholdType.StdDev:
                            sampleValue = activeRequests.Select<ActiveRequest, double>(sample => (sample.ExecutionTime / 1000)).StandardDeviation();
                            break;
                        case ExecutionThresholdType.PStdDev:
                            sampleValue = activeRequests.Select<ActiveRequest, double>(sample => (sample.ExecutionTime / 1000)).StandardDeviationP();
                            break;
                        case ExecutionThresholdType.Variance:
                            sampleValue = activeRequests.Select<ActiveRequest, double>(sample => (sample.ExecutionTime / 1000)).Variance();
                            break;
                        case ExecutionThresholdType.PVariance:
                            sampleValue = activeRequests.Select<ActiveRequest, double>(sample => (sample.ExecutionTime / 1000)).VarianceP();
                            break;
                        default: //ExecutionThresholdType.Max
                            sampleValue = activeRequests.Max(sample => (sample.ExecutionTime / 1000));
                            break;
                    }
                    normalizedRequestExecutionTime = Math.Round(sampleValue, MidpointRounding.AwayFromZero);

                    PlotModeToolStripStatusLabel.Text = string.Format(PLOT_MODE_FORMAT, Enum.GetName(typeof(ExecutionThresholdType), executionThresholdType));
                }

                _perfSamples.Add(new PerfSample()
                {
                    SampleNumber = sampleNumber,
                    AvgExecutionTime = normalizedRequestExecutionTime
                });

                RepaintChart();
            }
        }

        private ExecutionThresholdType GetExecutionThresholdType()
        {
            ExecutionThresholdType executionThresholdType = ExecutionThresholdType.Max; // default value
            string executionThresholdTypeName = Properties.Settings.Default.ExecutionThresholdType;
            if (!string.IsNullOrWhiteSpace(executionThresholdTypeName))
            {
                ExecutionThresholdType parsedExecutionThresholdType;
                bool isValidEnumName = Enum.TryParse<ExecutionThresholdType>(executionThresholdTypeName, true, out parsedExecutionThresholdType);
                if (isValidEnumName)
                {
                    executionThresholdType = parsedExecutionThresholdType;
                }
            }
            return executionThresholdType;
        }

        private void SetStatTypeLabel()
        {
            ExecutionThresholdType executionThresholdType = GetExecutionThresholdType();
            PlotModeToolStripStatusLabel.Text = string.Format(PLOT_MODE_FORMAT, Enum.GetName(typeof(ExecutionThresholdType), executionThresholdType));
        }

        private void RepaintChart()
        {
            // Render the chart:
            PerfChart.DataSource = _perfSamples;
            PerfChart.Series[0].XValueMember = "SampleNumber";
            PerfChart.Series[0].YValueMembers = "AvgExecutionTime";

            PerfChart.DataBind();
        }

        private void StopTimer()
        {
            // Dispose of the timer.
            if (_pollingTimer != null)
            {
                if (_pollingTimer.Enabled)
                    _pollingTimer.Stop();
                _pollingTimer.Dispose();
                _pollingTimer = null;
            }
        }

        private void PopulateActiveRequests(bool isManualRefresh)
        {
            try
            {
                TfsActiveRequestsDataGridView.DataSource = null;
                TfsActiveRequestsDataGridView.Rows.Clear();

                AdministrationWebServiceSoapClient client = null;

                string[] remoteAddresses = Utility.GetRemoteAddresses();

                if (remoteAddresses.Length == 0)
                {
                    // Use the default remote address for the endpoint.
                    using (client = new AdministrationWebServiceSoapClient())
                    {
                        remoteAddresses = new string[] { client.Endpoint.Address.Uri.AbsoluteUri };
                    }
                }

                string hostNames = Utility.GetTfsHostNamesForDisplay(remoteAddresses);
                HostsToolStripStatusLabel.Text = hostNames;
                this.Text = string.Format(TITLE_TEMPLATE, hostNames);

                bool addedNewNotifications = false;
                List<ActiveRequest> requests = new List<ActiveRequest>();

                foreach (string remoteAddress in remoteAddresses)
                {
                    using (client = new AdministrationWebServiceSoapClient("AdministrationWebServiceSoap", remoteAddress))
                    {
                        TeamFoundationServiceHostActivity[] activeRequestsInAllHosts = null;
#if _TFS2010_
                        activeRequestsInAllHosts = client.QueryActiveRequests(null, true);
#else
                        Guid[] hostIds = Utility.GetHostIdFromUri(Utility.GetTfsHostNameFromServiceUri(remoteAddress));
                        activeRequestsInAllHosts = client.QueryActiveRequests(hostIds, true);
#endif

                        if (activeRequestsInAllHosts != null && activeRequestsInAllHosts.Length > 0)
                        {
                            foreach (var request in activeRequestsInAllHosts)
                            {
                                if (request != null && request.ActiveRequests != null && request.ActiveRequests.Length > 0)
                                {
                                    foreach (var activeRequest in request.ActiveRequests)
                                    {
                                        ActiveRequest currentActiveRequest = new ActiveRequest()
                                            {
                                                RequestId = activeRequest.RequestId,
                                                MethodName = activeRequest.MethodName,
                                                UserName = activeRequest.UserName,
                                                StartTime = activeRequest.StartTime.ToLocalTime(),
                                                ExecutionTime = activeRequest.ExecutionTime,
                                                QueuedTime = activeRequest.QueuedTime,
                                                Queued = activeRequest.Queued,
                                                RemoteComputer = activeRequest.RemoteComputer,
                                                RemotePort = activeRequest.RemotePort,
                                                HostId = request.Id,
                                                ServerActivityName = request.Name,
                                                ServerActivityStartTime = request.StartTime.ToLocalTime(),
                                                ServerActivityStatus = request.Status,
                                                ServerActivityStatusReason = request.StatusReason,
                                                RemoteAddress = remoteAddress,
                                                RequestInformation = activeRequest
                                            };
                                        requests.Add(currentActiveRequest);

                                        if (ShowNotificationsCheckBox.Checked
                                            && _executionThresholdInSeconds > 0
                                            && activeRequest.ExecutionTime > _executionThresholdInSeconds * 1000)
                                        {
                                            AddActiveRequestNotification(currentActiveRequest);
                                            addedNewNotifications = true;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (requests.Count > 0)
                    {
                        TfsActiveRequestsDataGridView.AutoGenerateColumns = true;
                        TfsAdministrationBindingSource.DataSource = requests.OrderByDescending((request) => request.ExecutionTime);
                        TfsActiveRequestsDataGridView.DataSource = TfsAdministrationBindingSource;
                        TfsActiveRequestsDataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
                        TfsActiveRequestsDataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders;
                        TfsActiveRequestsDataGridView.Columns["RequestInformation"].Visible = false;

                        AddPerfSample(requests.ToArray());
                    }
                    else
                    {
                        AddPerfSample(null);
                    }

                    DateTime lastUpdatedDateTime = DateTime.Now;
                    RequestsStatusStrip.Items[0].Text = string.Format(LAST_UPDATED_TEMPLATE, lastUpdatedDateTime.ToLongDateString(), lastUpdatedDateTime.ToLongTimeString());

                    if (addedNewNotifications)
                    {
                        notificationsToolStripSplitButton.Visible = true;
                        if (!TrayNotifyIcon.Visible)
                        {
                            TrayNotifyIcon.Visible = true;
                            TrayNotifyIcon.ShowBalloonTip(5000,
                                "TFS Request Manager - Threshold Exceeded",
                                "One or more notifications were added due to exceeding the execution time threshold.\r\n" +
                                "Click 'View Notifications' to view the notifications list.",
                                ToolTipIcon.Warning);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (isManualRefresh || (!isManualRefresh && !ShowNotificationsCheckBox.Checked))
                {
                    MessageBox.Show(string.Format("An error occurred while populating active requests:\r\n{0}\r\n{1}", ex.Message, ex.StackTrace),
                        "Error Populating Active Requests",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error,
                        MessageBoxDefaultButton.Button1);
                }
                else
                {
                    AddErrorNotification(ex);

                    notificationsToolStripSplitButton.Visible = true;
                    TrayNotifyIcon.Visible = true;
                    lock (_lastErrorAddedSyncLock)
                    {
                        // Only show the error balloon again if a threshold has been exceeded.
                        // This is to avoid generating a flood of ballons.
                        if (_lastErrorAddedDateTime.AddSeconds(60) < DateTime.Now)
                        {
                            TrayNotifyIcon.ShowBalloonTip(5000,
                                "TFS Request Manager - Error Occurred",
                                string.Format("{0}.\r\n" +
                                "Click 'View Notifications' to view the error details.", ex.Message),
                                ToolTipIcon.Error);
                            _lastErrorAddedDateTime = DateTime.Now;
                        }
                    }
                }
            }
        }

        private void AddActiveRequestNotification(ActiveRequest currentActiveRequest)
        {
            lock (_notificationsSyncLock)
            {
                if (_notifications.Count >= _maximumNotificationsListSize)
                {
                    // Delete the oldest element.
                    _notifications.Remove(_notifications.Keys[0]);
                }
                _notifications.Add(DateTime.Now.ToString(), string.Format(THRESHOLD_EXCEEDED_TEMPLATE, _executionThresholdInSeconds, currentActiveRequest));
            }
        }

        private void AddErrorNotification(Exception exception)
        {
            lock (_notificationsSyncLock)
            {
                if (_notifications.Count >= _maximumNotificationsListSize)
                {
                    // Delete the oldest element.
                    _notifications.Remove(_notifications.Keys[0]);
                }
                _notifications.Add(DateTime.Now.ToString(), string.Format(WEB_SERVICE_ERROR_TEMPLATE, exception.Message, exception.StackTrace));
            }
        }

        private void ShowNotifications()
        {
            this.Activate();
            Notifications notificationsForm = new Notifications();
            notificationsForm.NotificationMessages = _notifications;
            notificationsForm.ShowDialog();
        }

        #endregion
    }
}
